package gate

import (
	"Alpaca/chanrpc"
	"Alpaca/log"
	"Alpaca/network"
	"net"
	"reflect"
)

type Gate struct {
	MaxConn      int
	MaxMsgLen    uint32
	WriteChanLen int
	Processor    network.Processor
	AgentChanRpc *chanrpc.Server
	//tcp
	TcpAddr string
}

func (gate *Gate) Run(closeFlag chan bool) {
	var tcpServer *network.TcpServer
	if gate.TcpAddr != "" {
		tcpServer = new(network.TcpServer)
		tcpServer.Addr = gate.TcpAddr
		tcpServer.MaxConn = gate.MaxConn
		tcpServer.MaxMsgLen = gate.MaxMsgLen
		tcpServer.WriteChanLen = gate.WriteChanLen
		tcpServer.NewAgent = func(conn *network.TcpConn) network.Agent {
			a := &agent{conn: conn, gate: gate}
			if gate.AgentChanRpc != nil {
				gate.AgentChanRpc.Go("NewAgent", a)
			}
			return a
		}
	}

	if tcpServer != nil {
		tcpServer.Start()
	}

	<-closeFlag
	//log.Debug("get end flag")

	if tcpServer != nil {
		tcpServer.Close()
	}
}

type agent struct {
	conn     network.Conn
	gate     *Gate
	userData interface{}
}

func (a *agent) Run() {
	for {
		data, err := a.conn.ReadMsg()
		if err != nil {
			log.Debug("read message error: %v", err)
			break
		}

		if a.gate.Processor != nil {
			msg, err := a.gate.Processor.UnMarShal(data)
			if err != nil {
				log.Debug("unmarshal message error: %v", err)
				break
			}

			err = a.gate.Processor.Route(msg, a)
			if err != nil {
				log.Debug("route message error: %v", err)
				break
			}
		}
	}
}

func (a *agent) OnClose() {
	if a.gate.AgentChanRpc != nil {
		err := a.gate.AgentChanRpc.Call0("CloseAgent", a)
		if err != nil {
			log.Error("chanrpc error: %v", err)
		}
	}
}

func (a *agent) WriteMsg(msg interface{}) {
	if a.gate.Processor != nil {
		data, err := a.gate.Processor.MarShal(msg)
		if err != nil {
			log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err)
			return
		}
		err = a.conn.WriteMsg(data)
		if err != nil {
			log.Error("write message %v error: %v", reflect.TypeOf(msg), err)
		}
	}
}

func (a *agent) LocalAddr() net.Addr {
	return a.conn.LocalAddr()
}

func (a *agent) RemoteAddr() net.Addr {
	return a.conn.RemoteAddr()
}

func (a *agent) Close() {
	a.conn.Close()
}

func (a *agent) Destroy() {
	a.conn.Destroy()
}

func (a *agent) UserData() interface{} {
	return a.userData
}

func (a *agent) SetUserData(data interface{}) {
	a.userData = data
}
